<?php
/**
 * Created by PhpStorm
 * User: 龍zero
 * Date: 2022-04-10
 * Time: 16:28
 */
declare(strict_types=1);

namespace App\Middleware;

use App\Collector\PermissionMetaCollector;
use App\Exception\AuthenticationException;
use App\Lib\Enum\UserLevel;
use App\Lib\Tools\LocalUser;
use App\Model\LinPermission;
use App\Service\Contract\ITokenProviderService;
use App\Service\GroupService;
use App\Service\UserService;
use Closure;
use FastRoute\Dispatcher;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\Server\Exception\ServerException;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function Hyperf\Support\env;

class AuthorizeMiddleware implements MiddlewareInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;


    /**
     * @Inject()
     * @var PermissionMetaCollector
     */
    private $metaCollector;

    /**
     * @Inject()
     * @var GroupService
     */
    private $groupService;

    /**
     * @Inject()
     * @var UserService
     */
    private $userService;

    /**
     * @Inject()
     * @var ITokenProviderService
     */
    private $tokenProvider;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }


    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $dispatched = $request->getAttribute(Dispatched::class);
      
        if (! $dispatched instanceof Dispatched) {
            throw new ServerException(sprintf('The dispatched object is not a %s object.', Dispatched::class));
        }
        if($this->shouldHandle($dispatched)){
            $dispatchedHandler = $dispatched->handler->callback;
            if (is_array($dispatchedHandler) && isset($dispatchedHandler[0], $dispatchedHandler[1])) {
                [$controller,$method] = $dispatchedHandler;
                #定义meta验证key
                $identity = $controller.'#'.$method;
                #获取接口的用户等级
                $userLevel = $this->metaCollector->getUserLevel($identity);
                switch ($userLevel){
                    case UserLevel::ADMIN:
                        $this->handleAdmin();
                        break;
                    case UserLevel::LOGIN:
                        $this->handleLogin();
                        break;
                    case UserLevel::REFRESH:
                        $this->handleRefresh();
                        break;
                    case UserLevel::GROUP:
                        $permissionMeta = $this->metaCollector->getMeta($identity);
                        $this->handleGroup($permissionMeta);
                        break;
                    default:
                        break;
                }
            }
        }
        return $handler->handle($request);
    }


    protected  function handleAdmin(){
        $userId = $this->tokenProvider->getCurrentUID();
        if(!$this->verifyAdmin($userId)){
            throw new AuthenticationException(10001);
        }
        $this->getClaim($userId);
    }

    protected function handleLogin(){
        $userId =$this->tokenProvider->getCurrentUID();
        $this->getClaim($userId);
    }

    protected function handleRefresh(){
        $userId =$this->tokenProvider->getCurrentUID('refresh');
        $this->getClaim($userId);
    }

    protected function handleGroup($permissionMeta){
        $userId = $this->tokenProvider->getCurrentUID();
        $this->getClaim($userId);
        if(!$this->verifyAdmin($userId)){
            $myPermissions = $this->userService->getUserPermissions($userId);
            $matched = $myPermissions->contains(LinPermission::where('name',$permissionMeta['name'])->where('module',$permissionMeta['module'])->first());
            if(!$matched){
                throw new AuthenticationException(10001);
            }
        }
    }

    private function getClaim($userId){
        $user = $this->userService->getById($userId);
        if(!$user->avatar){
            $avatar = '';
        }elseif (substr($user->avatar,0,4) == 'http'){
            $avatar = $user->avatar;
        }else{
            $avatar = env('DOMAIN').env('FILE_STORE_DIR').'/'.$user->avatar;
        }
        $user->avatar = $avatar;
        LocalUser::setLocalUser($user);
    }

    private function verifyAdmin($userId){
       return $this->groupService->checkIsRootByUserId($userId);
    }

    protected function shouldHandle(Dispatched $dispatched): bool
    {
        return $dispatched->status === Dispatcher::FOUND && ! $dispatched->handler->callback instanceof Closure;
    }
}